package com.itbaizhan.escase.log;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.Date;
import java.util.UUID;

@Component
@Aspect
public class LogAop {

    //整个过程都是只有一个请求，把请求对象注入来
    @Autowired
    private HttpServletRequest request;

    //切点方法
    //在com.itbaizhan.escase包下的任意包的任意以Controller结尾的类下的任意方法
    @Pointcut("execution(* com.itbaizhan.escase.*.*Controller.*(..))")
    public void pointCut() {
    }

    //前置通知  pointCut()上面定义的切点 记录访问时间
    @Before("pointCut()")
    public void before() {
        request.setAttribute("visitTime", new Date());
    }

    //后置通知   生成日志对象
    // 用于拿到切点方法JoinPoint
    @AfterReturning("pointCut()")
    public void AfterReturning(JoinPoint joinPoint) {
        Date visitTime = (Date) request.getAttribute("visitTime");//访问时间
        long executionTime = new Date().getTime() - visitTime.getTime(); //访问时长
        String id = UUID.randomUUID().toString();
        String ip = request.getRemoteAddr();
        String url = request.getRequestURI();
        //切点方法J
        String method = joinPoint.getSignature().getName();
        int level = 0; //日志等级

        //将信息放进log对象
        Log log = new Log(id, visitTime, executionTime, ip, url, method, level);
       // System.out.println(log);
        printLog(log); //调用下面的printLon()把日志信息传到文件
    }


    //异常通知
    @AfterThrowing(value = "pointCut()",throwing = "ex")
    public void AfterThrowing(JoinPoint joinPoint,Throwable ex) {
        Date visitTime = (Date)request.getAttribute("visitTime");//访问时间
        long executionTime = new Date().getTime() - visitTime.getTime(); //访问时长
        String id = UUID.randomUUID().toString();
        String ip = request.getRemoteAddr();
        String url = request.getRequestURI();
        //切点方法
        String method = joinPoint.getSignature().getName();
        int level = 2; //日志等级

        //异常信息
        String message = ex.getMessage();
        //栈的异常信息  写法固定
        StringWriter sw = new StringWriter();
        PrintWriter printWriter = new PrintWriter(sw);
        ex.printStackTrace(printWriter);//将异常信息输入sw打印
        String stackTrace = sw.toString();
        //将信息放进log对象
        Log log = new Log(id, visitTime, executionTime, ip, url, method, level,message,stackTrace);
        //System.out.println(log);
        printLog(log);
    }

    //把数据转为json格式输出   放在方法外部，不用每次都 new ,否则浪费资源
    private ObjectMapper objectMapper = new ObjectMapper();
    //在application.yml中写文件路径，方便修改
    @Value("${logPath}")
    private String logPath;
    //写日志到文件中  只可以在这方法中把日志输也，外部的不可以（AfterReturning()方法要调用这里的方法，把日志信息传进来才可以打印信息）
    public void printLog(Log log){
        try {
            //要有true,否则会覆盖原有信息，只会有一条日志信息
            //System.setOut(new PrintStream(new FileOutputStream("f://myLog.log",true))); // 这样的文件位置固定了
            System.setOut(new PrintStream(new FileOutputStream(logPath,true)));
            String valueAsString = objectMapper.writeValueAsString(log);
            System.out.println(valueAsString);
        } catch (FileNotFoundException | JsonProcessingException e) {
            e.printStackTrace();
        }
    }

}